package winOrDie.Net;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Vector;


/**
 * This class provides WoD_UDP_Channel
 *
 */
public class WoD_UDP_Channel 
implements Runnable
{
	
	private int serverPort = 0;
	private int bufferLength = 0;
	private int timeOut = 0;
	
	
	/**
	 * Internal variable to stop this thread.
	 */
	private boolean fIsStopRequested = false;
	
	
	private DatagramSocket datagramSocket = null;

	private Vector<WoD_UDP_Channel_Listener> listeners = null;
	
	private Thread thread_DatagramSocket = null;
	
	
	public synchronized void add_WoD_UDP_Channel_Listener(WoD_UDP_Channel_Listener listener)
	{
		listeners.add(listener);
	}
	
	public synchronized void remove_WoD_UDP_Channel_Listener(WoD_UDP_Channel_Listener listener)
	{
		listeners.remove(listener);
	}
	
	protected void notify_WoD_UDP_Channel_Change(WoD_UDP_Channel_Event event)
	{
		for (WoD_UDP_Channel_Listener listener : listeners) {
			listener.on_WoD_UDP_Channel_Change(event);
		}
	}
	
	
	public WoD_UDP_Channel(int serverPort, int bufferLength, int timeOut)
	{
		this.serverPort = serverPort;
		this.bufferLength = bufferLength;
		this.timeOut = timeOut;
		
		listeners = new Vector<WoD_UDP_Channel_Listener>();
	}
	
	
	/**
	 * This public method initializes this {@link WoD_UDP_Channel}
	 * @throws Exception
	 */
	public void start_WoD_UDP_Channel() throws Exception
	{
		// Prepare datagram socket
		if (serverPort == 0)
		{
			datagramSocket = new DatagramSocket();
		}
		else
		{
			datagramSocket = new DatagramSocket(serverPort);	
		}
		
		datagramSocket.setSendBufferSize(bufferLength);
		//datagramSocket.setSoTimeout(timeOut);
		
		// Start thread for listen on DatagramSocket
		thread_DatagramSocket = new Thread(this);
		thread_DatagramSocket.setDaemon(true);
		thread_DatagramSocket.start();
	}
	
	public void stop_Thread()
	{
		datagramSocket.close();
		Thread.currentThread().interrupt();
	}
	
	@Override
	public void run() {
		
		DatagramPacket datagramPacket = null;
		WoD_UDP_Channel_Event woDUDPChannelEvent = null;
		
		while (!fIsStopRequested) {
			/**
			 * Prepare datagram packet to receive data
			 */
			datagramPacket = new DatagramPacket(
					new byte[bufferLength], // buffer 
					bufferLength); // length
			
			try {
				/**
				 * Receive message and notify to listeners
				 */
				datagramSocket.receive(datagramPacket);
				
				woDUDPChannelEvent = new WoD_UDP_Channel_Event(this);
				woDUDPChannelEvent.setEvent_Type(WoD_UDP_Channel_Event.MESSAGE_RECEIVED);
				woDUDPChannelEvent.setData(datagramPacket);
				notify_WoD_UDP_Channel_Change(woDUDPChannelEvent);
			} 
			catch (SocketTimeoutException e) {
				woDUDPChannelEvent = new WoD_UDP_Channel_Event(this);
				woDUDPChannelEvent.setEvent_Type(WoD_UDP_Channel_Event.ERROR);
				woDUDPChannelEvent.setData(e.getMessage());
				notify_WoD_UDP_Channel_Change(woDUDPChannelEvent);
			}
			catch (IOException e) {
				/*
				 * Fire IO error event
				 */
				
				woDUDPChannelEvent = new WoD_UDP_Channel_Event(this);
				woDUDPChannelEvent.setEvent_Type(WoD_UDP_Channel_Event.ERROR);
				woDUDPChannelEvent.setData(e.getMessage());
				notify_WoD_UDP_Channel_Change(woDUDPChannelEvent);
				
				fIsStopRequested = true;
				continue;
			}
			
		}
	}
	
	
	public void send_Message (
			String hostDirection,
			int udp_port,
			byte[] message
		) throws Exception
	{
		DatagramPacket datagramPacket = null;
		try {
			datagramPacket = new DatagramPacket(
					message, // buffer
					message.length, // length
					InetAddress.getByName(hostDirection), // direction
					udp_port // port
					);
		} catch (UnknownHostException e) {
			Exception exception = new Exception("Unkwon host " + hostDirection + ". " + e.getMessage());
			throw exception;
		} 
		
		try {
			datagramSocket.send(datagramPacket);
		} catch (Exception e) {
			Exception exception = new Exception("Cannot send datagram. " + e.getMessage());
			throw exception;
		}
	}
	

}
